package com.tfc.analysis.process;

import com.tfc.analysis.entity.Keyword;
import com.tfc.analysis.fragment.AbstractFragment;
import com.tfc.analysis.utils.AnalysisUtils;
import com.tfc.analysis.utils.StringUtils;

import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import java.util.regex.Pattern;

@SuppressWarnings("rawtypes")
public abstract class BaseFinderProcess implements Processor {
    /**
     * 每个词对应的白名单
     */
    private Map<Keyword, Set<Keyword>> wordToWhite = new HashMap<Keyword, Set<Keyword>>();
    /**
     * 最短的关键词长度。用于对短于这个长度的文本不处理的判断，以节省一定的效率
     */
    private int whiteWordMaxLen = 0;
    /**
     * 忽略内容组成的关键词树。
     */
    private Map<String, Map> ignoreTree;
    /**
     * 忽略内容的最长的词组合，用于性能改善
     */
    private int ignoreWordMaxLen = 0;

    public int getWhiteWordMaxLen() {
        return whiteWordMaxLen;
    }

    public void setWhiteWordMaxLen(int whiteWordMaxLen) {
        this.whiteWordMaxLen = whiteWordMaxLen;
    }

    public Map<Keyword, Set<Keyword>> getWordToWhite() {
        return wordToWhite;
    }

    public void setWordToWhite(Map<Keyword, Set<Keyword>> wordToWhite) {
        this.wordToWhite = wordToWhite;
    }

    public Map<String, Map> getIgnoreTree() {
        return ignoreTree;
    }

    public void setIgnoreTree(Map<String, Map> ignoreTree) {
        this.ignoreTree = ignoreTree;
    }

    public int getIgnoreWordMaxLen() {
        return ignoreWordMaxLen;
    }

    public void setIgnoreWordMaxLen(int ignoreWordMaxLen) {
        this.ignoreWordMaxLen = ignoreWordMaxLen;
    }

    /**
     * @param wordsTree 词表树
     * @param text      目标文本
     * @param fragment  每个命中的词处理器
     * @param minLen    词树中最短的词的长度
     * @return 返回文本处理结果，是一个Map结构，key是StringBuilder类型，是目标文本；value是Set&lt;String&gt;类型的找到的敏感词并处理后的结果。
     */
    @SuppressWarnings("unchecked")
    public Object process(Map<String, Map> wordsTree, String text, AbstractFragment fragment, int minLen) {
        StringBuilder result = new StringBuilder("");
        Set<String> collections = new HashSet<String>();
        Map<StringBuilder, Set<String>> ret = new HashMap<StringBuilder, Set<String>>();
        String pre = null;// 词的前面一个字
        while (true) {
            if (wordsTree == null || wordsTree.isEmpty() || StringUtils.isEmpty(text)) {
                result.append(text).toString();
                ret.put(result, collections);
                return ret;
            }
            if (text.length() < minLen) {
                result.append(text).toString();
                ret.put(result, collections);
                return ret;
            }
            String chr = text.substring(0, 1);
            text = text.substring(1);

            Map<String, Map> nextWord = wordsTree.get(chr);
            if (nextWord == null) {
                // 没有对应的下一个字，表示这不是关键词的开头，进行下一个循环
                result.append(chr);
                pre = chr;
                continue;
            }

            //找出对应的敏感词（要判定是否是白名单中的词）
            Keyword w = AnalysisUtils.getSensitiveWord(chr, pre, nextWord, text, getIgnoreTree(), ignoreWordMaxLen);
            if (w != null) {
                //如果词本身是纯ascii码，则要判断前后都不是ascii，防止误处理
                if (Pattern.matches("^\\w+$", w.getWord())) {
                    if ((w.getBefore() != null && Pattern.matches("^\\w+$", w.getBefore())) || (w.getAfter() != null && Pattern.matches("^\\w+$", w.getAfter()))) {//认为当前词不是单独词
                        w = null;
                    }
                }
            }
            if (w == null) {
                // 开头没有关键词，下一个循环
                result.append(chr);
                pre = chr;
                continue;
            }
            // 如果指定了要排除的词组，则进行判断
            if (!AnalysisUtils.isWhiteWord(w, getWordToWhite(), getWhiteWordMaxLen(), result, text, getIgnoreTree())) {
                collections.add(w.getWord());
                if (fragment != null) {
                    result.append(fragment.format(w));
                }
                text = cutWord(text, getIgnoreTree(), ignoreWordMaxLen, w.getWord().substring(1), result);
                //text = text.substring(w.getWordLength() - 1);
                pre = w.getWord().substring(w.getWordLength() - 1);
            } else {//如果是白名单，则
                result.append(chr);
                pre = chr;
            }
        }
    }

    /**
     * 将词从文本中切除，同时保留忽略内容
     *
     * @param text        还未处理的文本
     * @param matchedWord 匹配上的词
     * @param result      结果文本，即处理后的文本内容
     * @return
     */
    private String cutWord(String text, Map<String, Map> ignoreTree, int ignoreWordMaxLen, String matchedWord, StringBuilder result) {
        //从匹配上的词开始一个字一个字的对比，如果某个字的定位>0，则之前的内容就是忽略的内容，添加到result中
        int idx = -1;
        for (char c : matchedWord.toCharArray()) {
            //先去除开头的忽略内容
            String tmp = AnalysisUtils.cutStartIgnore(text, ignoreTree, ignoreWordMaxLen);
            String i = text.replace(tmp, "");
            result.append(i);
            text = tmp;
            idx = text.indexOf(c);
            if (idx == 0) {
                //说明是正常的状态，直接将该内容砍掉即可
                text = text.substring(1);
            } else if (idx > 0) {
                result.append(text.substring(0, idx));
                text = text.substring(idx + 1);
            } else {
                //应该不会运行到这个地方，因为词肯定会被匹配上了
                break;
            }
        }
        return text;
    }
}
